package main

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"io"
	"os"
)

import bencode "github.com/jackpal/bencode-go"

type FileDict struct {
	Length int64    "length"
	Path   []string "path"
	Md5sum string   "md5sum"
}

type InfoDict struct {
	FileDuration []int64 "file-duration"
	FileMedia    []int64 "file-media"

	// Single file
	Name   string "name"
	Length int64  "length"
	Md5sum string "md5sum"

	// Multiple files
	Files       []FileDict "files"
	PieceLength int64      "piece length"
	Pieces      string     "pieces"
	Private     int64      "private"
}

type MetaInfo struct {
	Info         InfoDict   "info"
	InfoHash     string     "info hash"
	Announce     string     "announce"
	AnnounceList [][]string "announce-list"
	CreationDate int64      "creation date"
	Comment      string     "comment"
	CreatedBy    string     "created by"
	Encoding     string     "encoding"
}

func (metaInfo *MetaInfo) ReadTorrentMetaInfoFile(r io.Reader) bool {

	fileMetaData, er := bencode.Decode(r)
	fmt.Println("ReadTorrentMetaInfoFile. Decode status:", er)
	if er != nil {
		return false
	}

	metaInfoMap, ok := fileMetaData.(map[string]interface{})
	if !ok {
		return false
	}

	var bytesBuf bytes.Buffer
	for mapKey, mapVal := range metaInfoMap {
		switch mapKey {
		case "info":
			if er = bencode.Marshal(&bytesBuf, mapVal); er != nil {
				return false
			}

			infoHash := sha1.New()
			infoHash.Write(bytesBuf.Bytes())
			metaInfo.InfoHash = string(infoHash.Sum(nil))

			if er = bencode.Unmarshal(&bytesBuf, &metaInfo.Info); er != nil {
				return false
			}

		case "announce-list":
			if er = bencode.Marshal(&bytesBuf, mapVal); er != nil {
				return false
			}
			if er = bencode.Unmarshal(&bytesBuf, &metaInfo.AnnounceList); er != nil {
				return false
			}

		case "announce":
			if aa, ok := mapVal.(string); ok {
				metaInfo.Announce = aa
			}

		case "creation date":

			if tt, ok := mapVal.(int64); ok {
				metaInfo.CreationDate = tt
			}

		case "comment":
			if cc, ok := mapVal.(string); ok {
				metaInfo.Comment = cc
			}

		case "created by":
			if cb, ok := mapVal.(string); ok {
				metaInfo.CreatedBy = cb
			}

		case "encoding":
			if ed, ok := mapVal.(string); ok {
				metaInfo.Encoding = ed
			}
		}
	}

	return true
}

func parseTorrent(filename string) (int, error) {

	fp, err := os.Open(filename)
	if err != nil {
		fmt.Println("打开文件 "+filename+" 错误:", err)
	}
	defer fp.Close()

	var metaTorrent MetaInfo
	ok := metaTorrent.ReadTorrentMetaInfoFile(fp)
	fmt.Println("解析文件结束：", ok)
	if !ok {
		return 3, nil
	}

	fmt.Println("开始读取数据")
	name := metaTorrent.Info.Name
	fmt.Println("Name: " + name)

	hashInfo := fmt.Sprintf("%X", metaTorrent.InfoHash)
	created := metaTorrent.CreationDate

	var fileLength int64
	var total int
	total = 0 // 文件数量
	var fileDownLoadList bytes.Buffer
	var fileList string

	for _, fileDict := range metaTorrent.Info.Files {
		fileLength += fileDict.Length
		for _, path := range fileDict.Path {
			fileDownLoadList.WriteString(path)
			fileDownLoadList.WriteString(",")
			total = total + 1
		}
	}
	fileList = fileDownLoadList.String()

	var fileLengthTotal int64
	if fileLength > 0 {
		fileLengthTotal = fileLength //Bytes / (1024 * 1024)
	}

	if fileLengthTotal > 0 {
		fmt.Println(name, "\r\n", hashInfo, "\r\n", created, "\r\n", fileLengthTotal, "\r\n", fileList, "\r\n", total, "\r\n")
	}

	return 0, nil
}

func main() {

	if len(os.Args) != 3 {
		println("Usage:", os.Args[0], "SubFolder Filename")
		os.Exit(1)
	}

	var fileName string
	if os.Args[1] == "-" {
		fileName = os.Args[2]
	} else {
		fileName = "F:/torrent/" + os.Args[1] + "/" + os.Args[2]
	}

	parseTorrent(fileName)
}
